<cfcomponent>

	<cfset variables.context = StructNew()>
	<cfset context.newline = true>
	<cfset context.contents = false>
	<cfset context.intable = false>
	<cfset context.para = false>

	<cffunction name="parse" access="public" returntype="string" output="false">
		<cfargument name="wikiSyntax" type="string" required="yes">

		<cfset var myResult = "">

		<cfset var wikiInput = ListToArray(wikiSyntax, "#chr(10)#", true)>
		<cfset var wikiInput = ArrayNew(1)>

		<cfset Replace(wikiSyntax, Chr(10), "", "ALL")>
		<cfset findBreak = Find(Chr(13), wikiSyntax)>
		<cfset lastBreak = 1>
		<cfloop condition="findBreak GT 0">
			<cfset ArrayAppend(wikiInput, mid(wikiSyntax, lastBreak, findBreak-lastBreak))>
			<cfset lastBreak = findBreak+1>
			<cfset findBreak = Find(Chr(13), wikiSyntax, lastBreak)>
		</cfloop>

		<!--- <cfdump var="#wikiInput#"> --->

		<cfset fInCode = false>
		<cfset fInTable = false>
		<cfset fInList = false>
		<cfset fInListNum = false>
		<cfset fInNormal = true>
		<cfset prevInCode = false>
		<cfset prevHeader = false>
		<cfset prevNormal = true>
		<cfset listDepth = "">

		<cfdump var="#wikiSyntax#" label="Raw Wiki Syntax">
		<cfloop array="#wikiInput#" index="line">
			<cfoutput>[[#line#]]<br></cfoutput>
		</cfloop>

		<cfloop array="#wikiInput#" index="line">
			<cfset leftOne = left(line, 1)>
			<cfset leftTwo = "">

			<!--- manage empty lines;
				causing breaks in normal text
				closing lists (if in a list)
				closing tables (if in a table)
			--->
			<cfif trim(line) IS "">
				<cfif fInCode>
					<cfset myResult &= "<br>">
				<cfelseif fInList>
					<cfset myResult &= "</ul>">
					<cfset prevNormal = false>
					<cfset fInList = false>
					<cfset listDepth = "">
				<cfelseif fInListNum>
					<cfset myResult &= "</ol>">
					<cfset prevNormal = false>
					<cfset fInListNum = false>
					<cfset listDepth = "">
				<cfelseif fInTable>
					<cfset myResult &= "</table>">
					<cfset prevNormal = false>
				<cfelseif prevNormal>
					<cfset myResult &= "</p>">
					<cfset prevNormal = false>
				</cfif>

			<!--- if the line is not empty, then proceed --->
			<cfelse>

				<cfif Len(line) GT 1><cfset leftTwo = left(line, 1)></cfif>

				<!--- check for heading --->
				<cfif Left(Trim(line), 1) IS "=" AND NOT fInTable AND NOT fInCode>
					<cfset myResult &= parseHeading(line)>
					<cfset prevNormal = false>
					<cfoutput>HEAD:</cfoutput>

				<!--- check for table markup --->
				<cfelseif leftTwo IS "||">
					<cfset fInTable = true>
					<cfset prevNormal = false>
					<cfset myResult &= parseTable(line)>
					<cfoutput>TABLE:</cfoutput>

				<!--- if we're entering a code statement --->
				<cfelseif NOT fInCode AND Left(Trim(line), 3) IS "{{{">
					<cfset fInCode = true>
					<cfset prevNormal = false>
					<cfset myResult &= "<pre>">

				<cfelseif fInCode AND Right(Trim(line), 3) IS "}}}">
					<cfset prevInCode = false>
					<cfset fInCode = false>
					<cfset prevNormal = false>
					<cfset myResult &= "</pre>">

				<!--- if we're in a code statement, then dump the data to a new line --->
				<cfelseif fInCode>
					<cfif prevInCode><cfset myResult &= "<br>"><cfelse><cfset prevInCode = true></cfif>
					<cfset myResult &= "#trim(line)#">
					<cfoutput>#line#</cfoutput>

				<!--- if we're dealing with a list item, check and flag the opening of the list and output the item --->
				<cfelseif Left(Trim(line), 1) IS "*">
					<cfset depth = REFind("^([ ]*)", line, 1, true)>
					<cfset line = Trim(line)>
					<cfif NOT fInList>
						<cfset myResult &= "<ul>">
						<cfset fInList = true>
					</cfif>
					<cfoutput><li>#line#</li></cfoutput>
					<cfif Len(line) GT 1>
						<cfset myResult &= "<li>#parseLine(Right(line, Len(line)-1))#</li>">
					</cfif>

				<!--- if we're dealing with a list item, check and flag the opening of the list and output the item --->
				<cfelseif Len(Trim(line)) GT 2 AND Left(Trim(line), 2) IS "1.">
					<cfset depth = REFind("^([ ]*)", line, 1, true)>
					<cfset currDepth = len(listDepth)>

					<cfdump var="#depth#"><cfoutput>#currDepth# [[#line#]]</cfoutput>

					<!--- if the depth is different from the current depth, then in/outdent the list --->
					<cfif currDepth LT depth.len[1]>
						<cfset myResult &= RepeatString("<ol>", depth.len[1]-currDepth)>
						<cfset listDepth &= RepeatString("N", depth.len[1]-currDepth)>
						<cfabort>
					<cfelseif currDepth GT depth.len[1]>
						<cfset myResult &= RepeatString("<ol>", depth.len[1]-currDepth)>
						<cfset listDepth = Left(listDepth, len(listDepth)-(depth.len[1]-currDepth))>
					</cfif>

					<cfset line = Trim(line)>
					<cfif NOT fInListNum>
						<cfset myResult &= "<ol>">
						<cfset fInListNum = true>
					</cfif>
					<cfoutput><li>#line#</li></cfoutput>
					<cfif Len(line) GT 2>
						<cfset myResult &= "<li>#parseLine(Right(line, Len(line)-2))#</li>">
					</cfif>

				<!--- lastly, close out any opened contexts --->
				<cfelse>
					<cfif NOT prevNormal>
						<cfset myResult &= "<p>">
						<cfset prevNormal = true>
					</cfif>
					<cfset myResult &= parseLine(line)>
					<cfoutput>LINE: </cfoutput>
				</cfif>

				<cfoutput>(#leftOne#) @@#line#@@ (code: #fInCode#; table: #fInTable#; list: #fInList#; num-list: #fInListNum#)<br></cfoutput>

				<!--- one of the last things to do is replace any table of TableOfContents, with the actual table of contents --->
				<!--- TODO: table of contents --->
			</cfif>
		</cfloop>

		<!---
		<cfoutput>#HTMLEditFormat(myResult)#</cfoutput><cfabort>
		--->

		<cfreturn myResult>
	</cffunction>


	<cffunction name="parseHeading" access="private" output="false">
		<cfargument name="line">

		<!--- regex the heading from the line --->
		<cfset heading = REFind("([=]+)([^=]*)([=]+)", line, 1, true)>

		<!--- check that four elements are returned --->
		<cfif ArrayLen(heading.len) NEQ 4>
			<cfset response = line>

		<!--- check that the 2nd and 4th lengths are the same, they're the H level --->
		<cfelseif heading.len[2] NEQ heading.len[4] OR heading.len[2] GT 6>
			<cfset response = line>

		<!--- assemble the response string, inserting the heading elements --->
		<cfelse>
			<cfset response = "<h#heading.len[2]#>#mid(line, heading.pos[3], heading.len[3])#</h#heading.len[2]#>">
		</cfif>

		<cfreturn response>
	</cffunction>

	<cffunction name="parseLine" access="private" output="false">
		<cfargument name="line">

		<cfset var response = line>
		<cfset var currentPos = 1>

		<!--- regex the heading from the line --->
		<cfset response = REReplace(response, "(^|\n| |[^'])'''''(.+?)'''''([^']?)", "\1<em><strong>\2</strong></em>\3", "ALL")>
		<cfset response = REReplace(response, "(^|\n| |[^'])'''(.+?)'''([^']?)", "\1<em>\2</em>\3", "ALL")>
		<cfset response = REReplace(response, "(^|\n| |[^'])''(.+?)''([^']?)", "\1<strong>\2</strong>\3", "ALL")>
		<cfset response = REReplace(response, "(^|\n| )?`([^`]*)`", "\1<span style=""font-family: Courier, 'Courier New', 'Andale Mono', Monaco, monospace"">\2</span>", "ALL")>

		<cfset code = REFind("(^|\n| [^{]){{{([^}]+)}}}", response, 1, true)>
		<cfif ArrayLen(code.pos) GT 2>
			<cfset response = "<pre class=""code"">#HTMLEditFormat(mid(response, code.pos[3], code.len[3]))#</pre>">
		</cfif>

		<!--- regex any links ([^\|\]]+)(|([^\]]+))] --->
		<cfset link = REFind("(^|\n| )\[\[([^\|\]]+)[|]?([^\]]*)]]", response, 1, true)>
		<cfif ArrayLen(link.len) GTE 4>
			<cfif link.len[4] EQ 0>
			   	<cfset response = REReplace(response, "(^|\n| )\[\[([^\|\]]+)[|]?([^\]]*)]]", " <a href=""\2"">\2</a>", "ALL")>
			<cfelse>
			   	<cfset response = REReplace(response, "(^|\n| )\[\[([^\|\]]+)[|]?([^\]]*)]]", " <a href=""\2"">\3</a>", "ALL")>
			</cfif>
		</cfif>

		<!---
		<cfset undr = Find("__", line, currentPos)>
		<cfset supr = Find("^", line, currentPos)>
		<cfset subr = Find(",,", line, currentPos)>
		<cfset smlr = Find("~-", line, currentPos)>
		<cfset lrgr = Find("~+", line, currentPos)>
		<cfset strk = Find("--", line, currentPos)>
		<cfif ArrayLen(mono.len) GT 1>
			<cfset response &= "<span style=""font-family: Courier, 'Courier New', 'Andale Mono', Monaco, monospace"">#mid(line, mono.pos[2], mono.len[2])#</span>">
		<cfelse>
			<cfset response = line>
		</cfif>
		--->

		<cfreturn response>
	</cffunction>

	<cffunction name="z_compressArray" access="private" output="false">
		<cfargument name="wikiInput">

		<cfset var response = ArrayNew(1)>
		<cfset var prevEmpty = true>
		<cfset var currentCell = 0>
		<cfset var fInCode = false>

		<cfloop array="#wikiInput#" index="line">
			<cfif trim(line) IS "" AND NOT fInCode>
				<cfset prevEmpty = true>
			<cfelseif prevEmpty>
				<cfset currentCell++>
				<cfset response[currentCell] = line>
				<cfset prevEmpty = false>
			<cfelse>
				<cfset response[currentCell] &= line>
			</cfif>

			<!--- if we're in a code statement, then we don't break on empty lines --->
			<cfif fInCode OR (NOT fInCode AND Left(Trim(line), 3) IS "{{{")>
				<cfset prevEmpty = false>
				<cfset fInCode = true>
				<cfoutput>#line#</cfoutput>: in code<br>
			</cfif>
			<cfif fInCode AND Right(Trim(line), 3) IS "}}}">
				<cfset prevEmpty = true>
				<cfset fInCode = false>
			</cfif>
		</cfloop>

		<cfreturn response>
	</cffunction>

</cfcomponent>